/*
 * CategoryClassificationDoubleCheck is a software package that uses 
 * data mining to verify that descriptions of items are classified 
 * into the correct categories.
 * 
 * Copyright (C) 2011  CommuniPatient, LLC
 * 
 * File: DataProcessor.java
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.unitvectory.categoryclassification;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Provides useful method for processing the data.
 * 
 * @author Jared Hatfield
 * 
 */
public class DataProcessor {

	/**
	 * Saves the files for the specified category.
	 * 
	 * @param main
	 *            The main location of the data.
	 * @param stopWords
	 *            The list of stop words to use.
	 * @param data
	 *            The data set.
	 * @param cat
	 *            The category.
	 * @return The location of the folder that was saved.
	 */
	public static File saveFiles(File main, List<String> stopWords, Data data,
			Category cat) {
		File loc = new File(main, "test-" + cat.getId());
		if (loc.isDirectory()) {
			deleteDir(loc);
		}

		loc.mkdirs();

		File fileIn = new File(loc, "in");
		fileIn.mkdir();

		File fileOut = new File(loc, "out");
		fileOut.mkdir();

		// Save all of the files
		for (int i = 0; i < data.getItems().size(); i++) {
			Item item = data.getItems().get(i);

			if (item.isInCategory(cat.getId())) {
				File f = new File(fileIn, item.getId() + ".txt");
				String body = item.getFilteredString(stopWords);
				writeFile(f, body);
			} else {
				File f = new File(fileOut, item.getId() + ".txt");
				String body = item.getFilteredString(stopWords);
				writeFile(f, body);
			}

		}

		return loc;
	}

	/**
	 * Reads in a file that contains all of the stop words.
	 * 
	 * @param filename
	 *            The filename.
	 * @return The list of stop words loaded from the file.
	 */
	public static List<String> getStopList(String filename) {
		List<String> words = new ArrayList<String>();
		try {
			File file = new File(filename);
			BufferedReader in = new BufferedReader(new FileReader(file));
			String line = null;
			while ((line = in.readLine()) != null) {
				if (line.length() > 0) {
					words.add(line.toLowerCase());
				}
			}
		} catch (Exception e) {
		}

		return words;
	}

	/**
	 * Load the XML document into memory.
	 * 
	 * @param file
	 *            The filename.
	 * @return The Data set.
	 */
	public static Data loadData(String filename) {
		Data data = new Data();
		try {
			File file = new File(filename);
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(file);
			doc.getDocumentElement().normalize();
			NodeList catLst = doc.getElementsByTagName("data").item(0)
					.getChildNodes();

			// Load all of the categories
			NodeList catActLst = getChildren(catLst, "categories");
			loadCategories(catActLst, data.getCategories());

			// Load all of the items
			NodeList itemsLst = doc.getElementsByTagName("item");
			for (int i = 0; i < itemsLst.getLength(); i++) {
				Node n = itemsLst.item(i);
				NodeList nl = n.getChildNodes();
				String id = getNodeValue(nl, "id");
				String name = getNodeValue(nl, "name");
				String description = getNodeValue(nl, "description");
				NodeList itemCats = getChildren(nl, "categories");
				Item item = new Item(id, name, description);
				loadCategories(itemCats, item.getCategories());
				data.getItems().add(item);
			}
		} catch (Exception e) {
			return null;
		}

		return data;
	}

	/**
	 * Crate a file with the specified contents.
	 * 
	 * @param file
	 *            The file.
	 * @param content
	 *            The content.
	 */
	public static void writeFile(File file, String content) {
		try {
			FileOutputStream fos = new FileOutputStream(file);
			OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8");
			out.write(content);
			out.close();
			fos.close();
		} catch (Exception e) {
		}
	}

	/**
	 * Delete a directory and all of the contents.
	 * 
	 * @param dir
	 *            The director to delete.
	 * @return True if deleted; otherwise false.
	 */
	public static boolean deleteDir(File dir) {
		if (dir.isDirectory()) {
			String[] children = dir.list();
			for (int i = 0; i < children.length; i++) {
				boolean success = deleteDir(new File(dir, children[i]));
				if (!success) {
					return false;
				}
			}
		}

		// The directory is now empty so delete it
		return dir.delete();
	}

	/**
	 * Gets all of the children by the requested name.
	 * 
	 * @param nodeLst
	 *            The node list.
	 * @param val
	 *            The lookup value.
	 * @return The children.
	 */
	private static NodeList getChildren(NodeList nodeLst, String val) {
		for (int i = 0; i < nodeLst.getLength(); i++) {
			Node n = nodeLst.item(i);
			if (n.getNodeName().equals(val)) {
				return n.getChildNodes();
			}
		}

		return null;
	}

	/**
	 * Gets the value for the specified node.
	 * 
	 * @param nodeLst
	 *            The node list.
	 * @param val
	 *            The lookup value.
	 * @return The parameter value.
	 */
	private static String getNodeValue(NodeList nodeLst, String val) {
		try {
			for (int i = 0; i < nodeLst.getLength(); i++) {
				Node n = nodeLst.item(i);
				if (n.getNodeName().equals(val)) {
					Element fstElmnt = (Element) n;
					return fstElmnt.getChildNodes().item(0).getNodeValue();
				}
			}
		} catch (Exception e) {
		}

		return "";
	}

	/**
	 * Loads all of the categories into the provided List.
	 * 
	 * @param catActLst
	 *            The node list.
	 * @param cats
	 *            The category collection.
	 */
	private static void loadCategories(NodeList catActLst, List<Category> cats) {
		for (int i = 0; i < catActLst.getLength(); i++) {
			Node n = catActLst.item(i);
			if (n.getNodeName().equals("category")) {
				NodeList catItemsLst = n.getChildNodes();
				String id = getNodeValue(catItemsLst, "id");
				String name = getNodeValue(catItemsLst, "name");
				Category cat = new Category(id, name);
				cats.add(cat);
			}
		}

	}
}
